import sys
import time
import pandas as pd

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.common.action_chains import ActionChains
from myselenium.locator import *
from myselenium.login_verify import Verification


class BasePage:
    LOGIN_PAGE_URL = "https://mkt.51job.com/tg/sem/LP_2022_BC.html?from=baidupz&partner=sem_pcbaidupz_2"
    LOGIN_FAIL_FLAG = "欢迎登录丨前程无忧（51Job）"

    def __init__(self):
        # 程序结束后不会自动关闭浏览器
        options = webdriver.EdgeOptions()
        options.add_experimental_option('detach', True)
        self.driver = webdriver.Edge(options=options)
        self.driver.get(self.LOGIN_PAGE_URL)
        self.driver.maximize_window()

        self.verification = Verification(self.driver)
        time.sleep(1)


class LoginPage(BasePage):
    PHONE_NUMBER = "15316565677"

    def __login_process_template(self):
        # 键入手机号
        self.enter_phone_number()
        # 获取验证码
        self.click_yzm_button()
        # 滑块验证
        self.verify()
        # 验证成功，进行验证码键入并点击登录按钮
        self.enter_yzm()
        self.click_login_button()
        # 有可能因为验证码输错而需要重新进行流程
        pre_title = self.driver.title
        if pre_title == self.LOGIN_FAIL_FLAG:
            # 会跳转到其他页面，故通过back进行刷新
            self.driver.back()
        time.sleep(5)

    def run_login_page_process(self):
        self.__login_process_template()

    def enter_phone_number(self):
        # 键入登陆用手机号
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(LoginPageLocators.PHONE_INPUT)
        )
        element = self.driver.find_element(*LoginPageLocators.PHONE_INPUT)
        element.clear()
        element.send_keys(self.PHONE_NUMBER)

    def click_yzm_button(self):
        # 点击获取验证码按钮
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(LoginPageLocators.YZM_BUTTON)
        )
        time.sleep(3)
        yzm_button = self.driver.find_element(*LoginPageLocators.YZM_BUTTON)
        yzm_button.click()

    def verify(self):
        # 进行验证
        img_element = self.get_verify_window()
        time.sleep(5)
        # 获取带有缺口待验证的图片
        print(img_element.location, img_element.size)
        self.verification.get_yzm_img_with_gap(img_element.location, img_element.size)
        # 获取原图
        print(img_element.location, img_element.size)
        self.verification.get_yzm_img_no_gap(img_element.location, img_element.size)
        # 计算需要滑动的距离
        distance = self.verification.find_slide_distance()
        # 根据距离对于滑块进行拖动
        self.slide_verify_block(distance)
        result_element = self.get_result_content()
        # 强制睡眠等待拖动滑块后结果栏文本刷新
        time.sleep(3)
        result_content = str(result_element.get_attribute("innerText"))

        chance = 0
        # 当验证失败时进行重复尝试
        while self.verification.is_verify_fail(result_content) and chance < self.verification.RECONNECTION_CHANCE:
            # 如果验证失败，先强制停止5s，再重新进行滑动操作以及距离修正
            time.sleep(5)
            self.slide_verify_block(self.verification.distance_modify(chance, distance))
            time.sleep(1)
            result_content = str(result_element.get_attribute("innerText"))

        if chance == self.verification.RECONNECTION_CHANCE:
            # 验证失败
            print("验证模块出现问题，请重启！")
            sys.exit(1)

    def slide_verify_block(self, distance):
        # 获取验证窗口上的滑块按钮
        WebDriverWait(self.driver, 10).until(ec.presence_of_element_located(LoginPageLocators.SLIDE_BUTTON))
        slide_button = self.driver.find_element(*LoginPageLocators.SLIDE_BUTTON)
        # 模拟人拖拽验证滑块
        slide_action = ActionChains(self.driver)
        # 点击，准备拖拽
        slide_action.click_and_hold(slide_button)
        slide_action.pause(0.2)
        # 拖拽时进行距离修正
        slide_action.move_by_offset(distance - 10, 0)
        slide_action.pause(0.6)
        slide_action.move_by_offset(10, 0)
        slide_action.pause(0.6)
        # 释放按钮
        slide_action.release()
        slide_action.perform()

    def get_verify_window(self):
        # 获取验证弹窗
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(LoginPageLocators.GEETEST_WINDOW)
        )
        img_element = self.driver.find_element(*LoginPageLocators.GEETEST_WINDOW)
        return img_element

    def get_result_content(self):
        # 寻找在滑动验证块滑动后显示成功信息或者错误信息的消息框
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(LoginPageLocators.RESULT_CONTENT)
        )
        result_element = self.driver.find_element(*LoginPageLocators.RESULT_CONTENT)
        return result_element

    def enter_yzm(self):
        # 键入验证码
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(LoginPageLocators.YZM_INPUT)
        )
        yzm_input = self.driver.find_element(*LoginPageLocators.YZM_INPUT)
        yzm_str = input("请输入验证码\n")
        while not self.verification.check_yzm_format(yzm_str):
            print("输入的验证码格式有误，请重试！")
            yzm_str = input("请输入验证码\n")
        yzm_input.clear()
        yzm_input.send_keys(yzm_str)

    def click_login_button(self):
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(LoginPageLocators.LOGIN_BUTTON)
        )
        self.driver.find_element(*LoginPageLocators.LOGIN_BUTTON).click()


class HomePage:

    def __init__(self, driver):
        self.driver = driver
        print(driver.title)

    def __homepage_process(self):
        self.click_search_link()
        time.sleep(5)

    def run_home_page_process(self):
        self.__homepage_process()

    def click_search_link(self):
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(HomePageLocators.JOB_SEARCH_LINK)
        )
        self.driver.find_element(*HomePageLocators.JOB_SEARCH_LINK).click()


class SearchPage:

    # def __init__(self):
    #     options = webdriver.EdgeOptions()
    #     options.add_experimental_option('detach', True)
    #     self.driver = webdriver.Edge(options=options)
    #     self.driver.get('https://we.51job.com/pc/search')
    #     self.driver.maximize_window()
    #     time.sleep(3)

    def __init__(self, driver):
        self.driver = driver
        time.sleep(3)

    def __screen_process_template(self):
        self.click_to_all_city()
        city_choices = [(0, 1), (0, 8), (4, 8)]
        self.screen_cities(city_choices, self.get_city_screen_tabs(), self.get_city_items())
        self.click_carry_box()
        # 当前筛选项为“在校生/应届生”  + "本科"
        choices = [(0, 1), (1, 4)]
        self.click_screen_item(choices, self.get_all_screen_item())
        # time.sleep(3)

    def __normal_browse_process_template(self):
        self.click_certain_job_blocks(self.get_all_jobs())

    def run_search_page_process(self):
        # self.get_related_job_func()
        self.__screen_process_template()
        self.__normal_browse_process_template()
        self.scroll_window(1)
        next_page_button = self.get_next_page_button()
        while next_page_button.is_enabled():
            next_page_button.click()
            time.sleep(5)
            self.scroll_window(100)
            self.__normal_browse_process_template()
            next_page_button = self.get_next_page_button()
        print("已经到最后一页了！")

    # 获得互联网技术一级标题下的所有分类标题字符串并存储到csv文件中
    def get_related_job_func(self):
        func_dict = dict()
        max_len = 0
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(SearchPageLocators.JOB_FUNC_BUTTON)
        )
        # 点击工作职能
        self.driver.find_element(*SearchPageLocators.JOB_FUNC_BUTTON).click()
        time.sleep(1)
        # 获得三个级联选项表
        menus = self.driver.find_elements(*SearchPageLocators.CASCADER_MENUS)
        # 点击互联网技术
        (menus[0].find_elements(*SearchPageLocators.JOB_FUNC_ITEMS))[1].click()
        # 点击按钮后页面元素将进行刷新，重新获取menus
        menus = self.driver.find_elements(*SearchPageLocators.CASCADER_MENUS)
        # 获得互联网技术二级menu并遍历
        sec_menus = menus[1].find_elements(*SearchPageLocators.JOB_FUNC_ITEMS)
        for sec_func in sec_menus:
            # 点击后三级menu才会刷新
            sec_func.click()
            time.sleep(1)
            reloader_menus = self.driver.find_elements(*SearchPageLocators.CASCADER_MENUS)
            third_funcs = reloader_menus[2].find_elements(*SearchPageLocators.JOB_FUNC_ITEMS)
            func_texts = list(func.text for func in third_funcs if func.text != "")
            time.sleep(3)
            pre_len = len(third_funcs)
            if pre_len > max_len:
                max_len = pre_len
            # 第三个menu中为滚动后动态加载的，故需要进行下拉加载
            if pre_len > 10:
                self.driver.execute_script('''document.getElementsByClassName("cascader_panel_menu")[2].scrollTop=230''')
                time.sleep(3)
                reload_third_funcs = reloader_menus[2].find_elements(*SearchPageLocators.JOB_FUNC_ITEMS)
                func_texts = func_texts + list(func.text for func in reload_third_funcs if func.text != "")
            print(func_texts)
        # 检查列表的长度是否一致，不一致则加为最长
            func_dict[sec_func.text] = func_texts
        for key in func_dict.keys():
            temp_list = func_dict[key]
            temp_len = len(temp_list)
            if temp_len != max_len:
                temp_list = temp_list + list('' for i in range(0, max_len - temp_len))
                func_dict[key] = temp_list

        data_frame = pd.DataFrame(func_dict)
        data_frame.to_csv(".\\results\\job_funcs.csv", encoding="utf_8_sig")

    # --------点击筛选部分--------
    def click_to_all_city(self):
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(SearchPageLocators.CITY_CHOICE_DIV)
        )
        self.driver.find_element(*SearchPageLocators.CITY_CHOICE_DIV).click()
        time.sleep(1)

    def get_city_screen_tabs(self):
        # 获得城市窗口左侧城市筛选表格
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(SearchPageLocators.CITY_SCREEN_TABS)
        )
        # 获取11个按字母的筛选方式
        tab_items = self.driver.find_elements(*SearchPageLocators.CITY_SCREEN_TABS)
        return tab_items

    def get_city_items(self):
        # 获得城市窗口右侧城市选择按钮
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(SearchPageLocators.CITY_GRID_ITEMS)
        )
        # 获取11个右侧城市筛选结果
        city_screen_items = list()
        grid_items = self.driver.find_elements(*SearchPageLocators.CITY_GRID_ITEMS)
        for item in grid_items:
            elements = item.find_elements(*SearchPageLocators.SPAN_ITEM)
            city_screen_items.append(elements)
        return city_screen_items

    def screen_cities(self, choices: list, tab_items: list, city_screen_items: list):
        tab_len = len(tab_items)
        count = 0
        for choice in choices:
            # 一次最多选五个
            if count == 5:
                break
            tab_num = choice[0]
            grid_num = choice[1]
            city_num = len(city_screen_items[tab_num])
            if tab_num < tab_len and grid_num < city_num:
                tab_items[tab_num].click()
                time.sleep(3)
                city_screen_items[tab_num][grid_num].click()
                time.sleep(3)
        self.click_city_certain_button()
        time.sleep(5)

    def click_city_certain_button(self):
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(SearchPageLocators.CITY_CERTAIN_BUTTON)
        )
        self.driver.find_element(*SearchPageLocators.CITY_CERTAIN_BUTTON).click()
        time.sleep(3)

    def click_carry_box(self):
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(SearchPageLocators.CARRY_BOX)
        )
        carry_box = self.driver.find_element(*SearchPageLocators.CARRY_BOX)
        carry_box.find_element(*SearchPageLocators.SPAN_ITEM).click()

    def get_all_screen_item(self):
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(SearchPageLocators.LAUNCH_BOX)
        )
        # 在点击"展开选项"之后会展开一个很多带有很多筛选的launch box
        launch_box = self.driver.find_element(*SearchPageLocators.LAUNCH_BOX)
        # 获得launch box中的的每一个筛选行
        # 5个筛选行分别为：[工作年限0, 学历要求1, 公司性质2, 公司规模3, 其他筛选4]
        f_box_list = launch_box.find_elements(*SearchPageLocators.F_BOX)
        # 遍历获得每个筛选行中的每个筛选元素
        screen_items = list()
        for box in f_box_list:
            items = box.find_elements(*SearchPageLocators.SPAN_ITEM)
            screen_items.append(items)
        return screen_items

    @staticmethod
    def click_screen_item(screen_choices: list, screen_items: list):
        # 最后一行元素不相同故不进行选择
        row_num = len(screen_items) - 1
        for choice in screen_choices:
            row = choice[0]
            column = choice[1]
            column_num = len(screen_items[row])
            if row < row_num and column < column_num:
                screen_items[row][column].click()
            time.sleep(3)
        time.sleep(3)
    # --------筛选部分结束--------

    def get_all_jobs(self):
        WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(SearchPageLocators.JOB_BLOCKS)
        )
        job_blocks_list = self.driver.find_elements(*SearchPageLocators.JOB_BLOCKS)
        return job_blocks_list

    def click_certain_job_blocks(self, job_blocks: list):
        # 模拟点击当前所得到的所有job元素块以遍历当前页码上找到的所有符合要求的岗位
        for job_block in job_blocks:
            job_block.click()
            time.sleep(10)
            self.driver.back()
            time.sleep(3)

    def scroll_window(self, start: int):
        for i in range(start, 9000, 100):
            self.driver.execute_script(f"document.documentElement.scrollTop={i}")
            time.sleep(0.2)

    def get_next_page_button(self):
        next_button = WebDriverWait(self.driver, 10).until(
            ec.presence_of_element_located(SearchPageLocators.NEXT_BUTTON)
        )
        return next_button
